package br.com.afi.util;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Essa classe realiza cálculos de datas.
 * 
 * @author André  de Fontana Ignacio
 * @author Ricardo Clementino
 * @author Thiago Assump��o da Costa
 * 
 * @version 1.0
 */
public class DateUtil
{
	/**
	 * Tipo ano.
	 */
	public static final short YEAR = 0;
	/**
	 * Tipo mês.
	 */
	public static final short MONTH = 1;
	/**
	 * Tipo dia;
	 */
	public static final short DAY = 2;

	/**
	 * Soma ou substrai valores na data informada.
	 * 
	 * @param date Data base para cálculo
	 * @param type Tipo de cálculo. Utilize as constantes definidas nesta classe
	 * @param count Quantidade do tipo informado, pode ser negativo.
	 * @return Data calculada (Calendar), ou null caso não seja possível realizar o cálculo
	 */
	public static Calendar calc( final Calendar date, final short type, final Integer count )
	{
		if ( date != null && count != null )
		{
			final Calendar aux = new GregorianCalendar( date.get( Calendar.YEAR ), date.get( Calendar.MONTH ), date.get( Calendar.DAY_OF_MONTH ) );
			switch ( type )
			{
				case YEAR:
					aux.add( Calendar.YEAR, count );
					break;

				case MONTH:
					aux.add( Calendar.MONTH, count );
					break;

				case DAY:
					aux.add( Calendar.DAY_OF_MONTH, count );
					break;
			}
			return aux;
		}
		return null;
	}

	/**
	 * Método que retorna a diferença em dias entre duas datas (Objetos do tipo Calendar).
	 * 
	 * @param dataIni Objeto Calendar com a data mais antiga
	 * @param dataFim Objeto Calendar com a data mais nova
	 * @return Diferença em dias entre duas datas, ou -1 caso as datas sejam inválidas
	 */
	public static int daysDiference( Calendar dataIni, Calendar dataFim )
	{
		if ( dataIni.getTime() != null && dataFim.getTime() != null && ! ( dataIni.getTime().after( dataFim.getTime() ) ) )
		{
			final long m1 = dataIni.getTimeInMillis();
			final long m2 = dataFim.getTimeInMillis();

			return ( int ) ( ( m2 - m1 ) / ( 24 * 60 * 60 * 1000 ) );
		}
		return -1;
	}

	/**
	 * Método que retorna a diferença em dias entre duas datas.
	 * 
	 * @param dataIni Objeto Calendar com a data mais antiga
	 * @param dataFim Objeto Calendar com a data mais nova
	 * @return Diferença em dias entre duas datas, ou -1 caso as datas sejam inválidas
	 */
	public static int daysDiference( Date dataIni, Date dataFim )
	{
		if ( dataIni != null && dataFim != null && ! ( dataIni.after( dataFim ) ) )
		{
			final long m1 = dataIni.getTime();
			final long m2 = dataFim.getTime();

			return ( int ) ( ( m2 - m1 ) / ( 24 * 60 * 60 * 1000 ) );
		}
		return -1;
	}

	/**
	 * Método que retorna a diferença em meses entre duas datas.
	 * 
	 * @param data1 Objeto Date com a data mais antiga
	 * @param data2 Objeto Date com a data mais nova
	 * @return Diferença em meses entre duas datas
	 */
	public static int monthsDiference( Date data1, Date data2 )
	{
		final Calendar g1 = new GregorianCalendar();
		final Calendar g2 = new GregorianCalendar();
		
		int meses = 0;
		boolean dataIniMaior = false;
		g1.setTime( data1 );
		g2.setTime( data2 );
		GregorianCalendar gc1, gc2;

		if ( g2.after( g1 ) )
		{
			gc2 = ( GregorianCalendar ) g2.clone();
			gc1 = ( GregorianCalendar ) g1.clone();
		}
		else
		{
			dataIniMaior = true;
			gc2 = ( GregorianCalendar ) g1.clone();
			gc1 = ( GregorianCalendar ) g2.clone();
		}

		gc1.clear( Calendar.MILLISECOND );
		gc1.clear( Calendar.SECOND );
		gc1.clear( Calendar.MINUTE );
		gc1.clear( Calendar.HOUR_OF_DAY );
		gc1.clear( Calendar.DATE );

		gc2.clear( Calendar.MILLISECOND );
		gc2.clear( Calendar.SECOND );
		gc2.clear( Calendar.MINUTE );
		gc2.clear( Calendar.HOUR_OF_DAY );
		gc2.clear( Calendar.DATE );

		while ( gc1.before( gc2 ) )
		{
			gc1.add( Calendar.MONTH, 1 );
			meses = dataIniMaior ? --meses : ++meses;
		}

		return ( meses == 0 || meses < 0 ) ? meses : meses - 1;
	}

	/**
	 * Obtém o ultimo dia do mês,
	 * 
	 * @param month Mês
	 * @param year Ano
	 * @return Ultimo dia do mês
	 */
	public static int getLastDayOfMonth( int month, int year )
	{
		final Calendar cal = new GregorianCalendar( year, month - 1, 1 );
		return cal.getActualMaximum( Calendar.DAY_OF_MONTH );
	}
}
